/*
* CIS 565
* Inclusive Scan
* Varun Sampath
* 
* This file contains the CPU reference implementation and the tests.
* The CUDA kernel is in scan_kernel.cu
*/

#if 0
// utilities and system includes
#include <cutil_inline.h>
#include <cuda_runtime_api.h>
#include <cutil_math.h>
#include <vector_types.h>

#include "scan.h"

/////////////////////////////////////////////////////////
///////////// CPU REFERENCE IMPLEMENTATION
/////////////////////////////////////////////////////////
template <typename T, ScanKind kind>
T * scan_gold(T* dev_src, const unsigned int len) {
	T * hst_src = new T[len];
	T * ans = new T[len];
	cutilSafeCall(cudaMemcpy(hst_src, dev_src, len * sizeof(T), cudaMemcpyDeviceToHost)); 
	if (kind == Forward) {
		ans[0] = hst_src[0];
		for (unsigned int i = 1; i < len; ++i) {
			ans[i] = ans[i-1] + hst_src[i];
		}
	}
	else if (kind == Backward) {
		ans[len-1] = hst_src[len-1];
		for (unsigned int i = len-1; i-- > 0; ) {
			ans[i] = ans[i+1] + hst_src[i];
		}
	}
	delete [] hst_src;
	return ans;
}

template <typename T>
T * sat_scan_gold(T* dev_src, const unsigned int width, const unsigned int height) {
	T * hst_src = new T[height][width];
	T * ans = new T[height][width];
	cutilSafeCall(cudaMemcpy(hst_src, dev_src, width * height * sizeof(T), cudaMemcpyDeviceToHost)); 
	for (unsigned int j = 0; j < height; j++) {
		ans[j][0] = hst_src[j][0];
		for (unsigned int i = 1; i < len; ++i) {
			ans[j][i] = ans[j][i-1] + hst_src[j][i];
		}
	}
	delete [] hst_src;
	return ans;
}

/////////////////////////////////////////////////////////
//////////// TEST HARNESS
/////////////////////////////////////////////////////////
int uploadToDevice(float * hst, const unsigned int len, 
				   float ** dev_ptr_ptr, unsigned int * len_ptr) {
					   float * dev = NULL;
					   cutilSafeCall(cudaMalloc((void**)&dev, sizeof(float)*len));
					   cutilSafeCall(cudaMemcpy(dev, hst, len * sizeof(float), cudaMemcpyHostToDevice));
					   *len_ptr = len;
					   *dev_ptr_ptr = dev;
					   return 0;
}

int generate(int num, float (*generator)(int index),
			 float ** dev_ptr_ptr, unsigned int  * len_ptr) {
				 float * hst = new float[num];
				 for (int i = 0; i < num; ++i) {
					 hst[i] = generator(i);
				 }
				 uploadToDevice(hst,num, dev_ptr_ptr, len_ptr);
				 delete [] hst;
				 return 0;
}

float gen1(int i) {
	return 2.0f*i;
}

float gen2(int i) {
	return 0.0f;
}

float gen3(int i) {
	return cos(i/1000.0f);
}

float gen4(int i) {
	return sin(i/100.0f);
}

float gen5(int i) {
	return 1.0f;
}

int setup1(float ** dev_ptr_ptr, unsigned int  * len_ptr) {
	const unsigned int NUM_PTS = 512;
	return generate(NUM_PTS,gen1,dev_ptr_ptr, len_ptr); 
}

int setup2(float ** dev_ptr_ptr, unsigned int  * len_ptr) {
	const unsigned int NUM_PTS = 300;
	return generate(NUM_PTS,gen2,dev_ptr_ptr, len_ptr); 
}

int setup3(float ** dev_ptr_ptr, unsigned int  * len_ptr) {
	const unsigned int NUM_PTS = 12000;
	return generate(NUM_PTS,gen3,dev_ptr_ptr, len_ptr); 
}

int setup4(float ** dev_ptr_ptr, unsigned int  * len_ptr) {
	const unsigned int NUM_PTS = 8193;
	return generate(NUM_PTS,gen4,dev_ptr_ptr, len_ptr); 
}

int setup5(float ** dev_ptr_ptr, unsigned int  * len_ptr) {
	const unsigned int NUM_PTS = 10;
	return generate(NUM_PTS,gen5,dev_ptr_ptr, len_ptr); 
}

template <ScanKind kind>
bool test(int (*setup)(float ** dev_ptr_ptr, unsigned int  * len_ptr)) {
	float * dev;
	unsigned int len;
	setup(&dev, &len);
	float * val = scan_gold<float, kind>(dev,len);
	cutilSafeCall(cudaFree(dev));
	setup(&dev, &len);
	float * yours = scan<float, kind>(dev,len);
	cutilSafeCall(cudaFree(dev));
	float EPSILON = 0.1f;

	bool isItGood = true;
	/*
	for (unsigned int i = 0; i < len; i++) {
	printf("%f, ", val[i]);
	}
	printf("\n");
	for (unsigned int i = 0; i < len; i++) {
	printf("%f, ", yours[i]);
	}
	printf("\n");
	*/
	for (unsigned int i = 0; i < len; i++) {
		if (!(abs(val[i] - yours[i]) < EPSILON)) {
			printf("V: %f Y: %f at %d\n", val[i], yours[i], i);
			isItGood = false;
			break;
		}
	}

	delete [] val;
	free(yours);
	return isItGood;
}

void report(char * tag, bool res) {
	printf("Test %s: %s\n", tag, res ? "PASSED" : "FAILED");
}

template <ScanKind kind>
int testScan() {
	report("1", test<kind>(setup1));
	report("2", test<kind>(setup2));
	report("3", test<kind>(setup3));
	report("4", test<kind>(setup4));
	report("5", test<kind>(setup5));
	return 0;
}

int
main( int argc, char** argv) {
	testScan<Forward>();
	return 0;
}
#endif